home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / games / IndiZone / oort / validate.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  33.6 KB  |  1,590 lines

  1. /*
  2.  * Copyright (C) 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*------------------------------------------------------------------------------
  18.  *
  19.  * @(#) - Oort - Host validation routines.
  20.  *
  21.  * $Id: validate.c,v 1.2 1994/01/28 00:22:16 mtj Exp $
  22.  *
  23.  * Chris Fouts - May, 1993.
  24.  *
  25.  *----------------------------------------------------------------------------*/
  26. #include <stdio.h>
  27. #include <stdarg.h>
  28. #include <malloc.h>
  29. #include <sys/types.h>
  30. #include <sys/socket.h>
  31. #include <netinet/in.h>
  32. #include <netdb.h>
  33. #include <bstring.h>
  34. #include <string.h>
  35. #include <arpa/inet.h>
  36. #include <time.h>
  37. #include <sys/time.h>
  38.  
  39. #include <X11/Intrinsic.h>
  40.  
  41. #include "oort.h"
  42. #include "gameio.h"
  43. #include "validate.h"
  44.  
  45. #define    MAX_DROPS    2
  46. #define    MAX_QUITS    3
  47. #define    MAX_ACKS    20
  48.  
  49. #define    NG_STEADY_CENSUS_RATE    400        /* tenths o' seconds */
  50. #define    NG_UNSTEADY_CENSUS_RATE     40        /* tenths o' seconds */
  51. #define NG_NEW_DEADLINE_TIME     50        /* seconds */
  52. #define NG_OLD_DEADLINE_TIME    120        /* seconds */
  53. #define    NG_WEEDING_TIME         15        /* seconds */
  54. #define    NG_DROPPING_DELTA    -30        /* seconds */
  55.  
  56. #define NG_VALID_UNKNOWN        0
  57. #define NG_VALID            1
  58. #define NG_INVALID            2
  59. #define NG_DROPPED            3
  60. #define NG_VALID_QUIT            4
  61.  
  62. struct ngHost {
  63.     long        id ;
  64.     short        agrees ;
  65.     short        drops ;
  66.     short        quits ;
  67.     short        acks ;
  68.     short        valid ;
  69.     short        last_valid ;
  70.     struct ngHost    *next ;
  71.     } ;
  72.  
  73. struct ngCensusList {
  74.     struct ngCensus        census ;
  75.     struct ngCensusList    *next ;
  76.     } ;
  77.  
  78. /* BEGIN PROTOTYPES -S validate.c */
  79. static struct ngHost *  addHost( long hostId ) ;
  80. static void             addToTime( struct timeval *dst, long secs,
  81.                             long usecs ) ;
  82. static int              compareCensus( struct ngCensus *input ) ;
  83. static void             copyBytes( void *src, void *dst, long n ) ;
  84. static void             copyTime( struct timeval *dst, struct timeval *src ) ;
  85. static void             doWeeding( void ) ;
  86. static long             elapsedSecTenths( struct timeval *bufThen,
  87.                             struct timeval *bufNow ) ;
  88. static struct ngHost *  findHost( long hostId ) ;
  89. static int              findHostInMyCensusList( long id ) ;
  90. static void             generateCensusChecksum( void ) ;
  91. static char *           ngPrintTimeHundreths( struct timeval *t ) ;
  92. static void             resetDeadlines( int except ) ;
  93. static void             sendAcknowledgement( long hostId ) ;
  94. static void             setTime( struct timeval *dst, struct timeval *src,
  95.                             long secs, long usecs ) ;
  96. static void             showHostValidity( struct ngHost *host ) ;
  97. /* END PROTOTYPES -S validate.c */
  98.  
  99.  
  100. static long            selfId = 0 ;
  101. static unsigned short        selfKey = 0 ;
  102. static char            *selfName ;
  103. static ngMagic            dropMagic ;
  104. static ngMagic            invalidateMagic ;
  105. static ngMagic            censusMagic ;
  106. static ngMagic            ackMagic ;
  107. static ngMagic            quitMagic ;
  108. static char            *ngLogFile = NULL ;
  109.  
  110. static struct ngHost        *firstHost = NULL ;
  111. static unsigned short        gameKey = 0 ;
  112. static struct timeval        last_census_sent ;
  113. static long            timeBetweenCensusPosts = 0 ;
  114. static Boolean            needToSendCensus ;
  115. static Boolean            needToDoWeeding = FALSE ;
  116. static struct timeval        weedTime ;
  117. static struct ngCensusList    myCensus ;
  118. extern unsigned int        numberPlayers ;
  119.  
  120. struct timeval            start_up_time ;
  121. struct timeval            currentTime ;
  122. struct timeval            deadline[MAXPLAYERS] ;
  123.  
  124.  
  125. /*------------------------------------------------------------------------------
  126.  * Inform validation routines of name and id.
  127.  *----------------------------------------------------------------------------*/
  128. void
  129. ngInit(
  130.     long        id,
  131.     unsigned short    key,
  132.     char        *name, 
  133.     ngMagic        drop,
  134.     ngMagic        invalidate,
  135.     ngMagic        census,
  136.     ngMagic        ack,
  137.     ngMagic        quit,
  138.     char        *logFile
  139.     )
  140. {
  141.     selfId = id ;
  142.     selfKey = key ;
  143.     selfName = strdup( name ) ;
  144.     dropMagic = drop ;
  145.     invalidateMagic = invalidate ;
  146.     censusMagic = census ;
  147.     ackMagic = ack ;
  148.     quitMagic = quit ;
  149.     ngLogFile = logFile ;
  150. }
  151.  
  152.  
  153.  
  154. /*------------------------------------------------------------------------------
  155.  * Called *only* when an ACKNOWLEDGEMENT packet is received (or in view-only
  156.  * mode).
  157.  *---------------------------------------------------------------------------*/
  158. int
  159. ngValidKeyedHost(
  160.     long        hostId,
  161.     unsigned short    key
  162.     )
  163. {
  164.     struct ngHost    *host ;
  165.  
  166.     if( key != gameKey )
  167.     {
  168.         return( 0 ) ;
  169.     }
  170.  
  171.     if( ( host = findHost( hostId ) ) == NULL )
  172.     {
  173.         if( ( host = addHost( hostId ) ) == NULL )
  174.         {
  175.             perror( "validKeyedHost" ) ;
  176.             endProgram( 1 ) ;
  177.         }
  178.     }
  179.  
  180.     if( host->valid == NG_VALID_UNKNOWN )
  181.     {
  182.         host->valid = NG_VALID ;
  183.         ngDprintf( "%s: acknowledged by %s\n",
  184.                   ngPrintTime(), ngHostNameFromId( hostId ) ) ;
  185.     }
  186.  
  187.     /*
  188.      * If host had dropped me, it's now receiving my packets, so send an
  189.      * ACK packet so it will know I see it and add me to its game.
  190.      */
  191.     else if( host->valid == NG_DROPPED )
  192.     {
  193.         host->valid = NG_VALID ;
  194.         sendAcknowledgement( hostId ) ;
  195.         ngDprintf( "%s: re-acknowledged by %s (had dropped me)\n",
  196.                   ngPrintTime(), ngHostNameFromId( hostId ) ) ;
  197.     }
  198.  
  199.     return( host->valid == NG_VALID ) ;
  200. }
  201.  
  202.  
  203.  
  204. /*------------------------------------------------------------------------------
  205.  * Returns TRUE if a host is valid.
  206.  *----------------------------------------------------------------------------*/
  207. int
  208. ngValidHost(
  209.     long        hostId
  210.     )
  211. {
  212.     struct ngHost    *host ;
  213.  
  214.     if( ( host = findHost( hostId ) ) == NULL )
  215.     {
  216.         return( 0 ) ;
  217.     }
  218.  
  219.     /*
  220.      * If host had quit, he has started back up again, so reset its status.
  221.      */
  222.     if( host->valid == NG_VALID_QUIT ) {
  223.         host->valid = NG_VALID_UNKNOWN ;
  224.         ngDprintf( "%s: receiving from %s again (had quit)\n",
  225.                   ngPrintTime(), ngHostNameFromId( hostId ) ) ;
  226.         }
  227.  
  228.     return( host->valid == NG_VALID ) ;
  229. }
  230.  
  231.  
  232.  
  233. /*------------------------------------------------------------------------------
  234.  * Find a host within the host list.
  235.  *----------------------------------------------------------------------------*/
  236. static struct ngHost *
  237. findHost(
  238.     long    hostId
  239.     )
  240. {
  241.     struct ngHost    *host = firstHost ;
  242.  
  243.     while( host != NULL )
  244.     {
  245.         if( host->id == hostId )
  246.         {
  247.             return( host ) ;
  248.         }
  249.         else
  250.         {
  251.             host = host->next ;
  252.         }
  253.     }
  254.  
  255.     return( host ) ;
  256. }
  257.  
  258.  
  259.  
  260. /*------------------------------------------------------------------------------
  261.  * Add a host to the host list.
  262.  *----------------------------------------------------------------------------*/
  263. static struct ngHost *
  264. addHost(
  265.     long    hostId
  266.     )
  267. {
  268.     struct ngHost    *host = firstHost ;
  269.     struct ngHost    *prev = NULL ;
  270.  
  271.     while( host != NULL )
  272.     {
  273.         prev = host ;
  274.         host = host->next ;
  275.     }
  276.  
  277.     if( ( host = (struct ngHost *)malloc( sizeof( struct ngHost ) ) ) !=
  278.         NULL )
  279.     {
  280.         host->id = hostId ;
  281.         host->agrees = 0 ;
  282.         host->drops = 0 ;
  283.         host->quits = 0 ;
  284.         host->acks = 0 ;
  285.         host->valid = NG_VALID_UNKNOWN ;
  286.         host->last_valid = NG_VALID_UNKNOWN ;
  287.         host->next = NULL ;
  288.         if( firstHost == NULL )
  289.         {
  290.             firstHost = host ;
  291.         }
  292.         ngDprintf( "%s: started receiving from %s\n",
  293.                   ngPrintTime(), ngHostNameFromId( hostId ) ) ;
  294.     }
  295.  
  296.     if( prev != NULL )
  297.     {
  298.         prev->next = host ;
  299.     }
  300.  
  301.     return( host ) ;
  302. }
  303.  
  304.  
  305.  
  306. /*------------------------------------------------------------------------------
  307.  * Drop a host when I haven't received any packets in a while from it.
  308.  *---------------------------------------------------------------------------*/
  309. void
  310. ngDropHost(
  311.     long    hostId
  312.     )
  313. {
  314.     struct ngHost    *host ;
  315.     struct ngAck    invalid ;
  316.  
  317.     if( ( host = findHost( hostId ) ) == NULL )
  318.     {
  319.         return ;
  320.     }
  321.     host->drops += 1 ;
  322.  
  323.     if( host->drops > MAX_DROPS )
  324.     {
  325.         ngInvalidateHost( hostId ) ;
  326.     }
  327.     else
  328.     {
  329.         /*
  330.          * Notify host of being dropped.
  331.          */
  332.         invalid.magic = dropMagic ;
  333.         invalid.id = selfId ;
  334.         invalid.ackId = hostId ;
  335.         invalid.key = gameKey ;
  336.         sendOut( &invalid, sizeof( invalid ) ) ;
  337. #if defined( NETDEBUGGER )
  338.         post_new_message( 0, 0, "sent drop packet to %c", 'a' + hostId ) ;
  339. #endif /* defined( NETDEBUGGER ) */
  340.  
  341.         /*
  342.          * Reset the host so that when I start receiving packets from it
  343.          * again, I'll acknowledge it.
  344.          */
  345.         host->valid = NG_VALID_UNKNOWN ;
  346.         host->acks = 0 ;
  347.         ngDprintf( "%s: dropping %s from game (stopped getting "
  348.             "packets)\n", ngPrintTime(),
  349.             ngHostNameFromId( hostId ) ) ;
  350.         }
  351. }
  352.  
  353.  
  354.  
  355. /*------------------------------------------------------------------------------
  356.  * Remove a host from our census list.
  357.  *----------------------------------------------------------------------------*/
  358. void
  359. ngRemoveHostFromCensus(
  360.     long    id
  361.     )
  362. {
  363.     int            i ;
  364.     int            n ;
  365.     struct ngCensusList    *next = myCensus.next ;
  366.     struct ngCensusList    *prev = &myCensus ;
  367.  
  368.     n = findHostInMyCensusList( id ) ;
  369.  
  370.     while( next && next->census.id != id ) {
  371.         prev = next ;
  372.         next = next->next ;
  373.         }
  374.  
  375.     if( n < 0 || next == NULL ) {
  376.         return ;
  377.         }
  378.  
  379.     prev->next = next->next ;
  380.  
  381.     free( next ) ;
  382.  
  383.     myCensus.census.nPlayers-- ;
  384.  
  385.     copyBytes( &(myCensus.census.list[n+1]), &(myCensus.census.list[n]),
  386.         (myCensus.census.nPlayers - n) *
  387.         sizeof( myCensus.census.list[0] ) );
  388.  
  389.     generateCensusChecksum() ;
  390.  
  391.     needToSendCensus = TRUE ;
  392.  
  393.     i = findPlayer( id ) ;
  394.     for( n = i ; n < numberPlayers ; n++ )
  395.     {
  396.         deadline[n] = deadline[n+1] ;
  397.     }
  398. }
  399.  
  400.  
  401.  
  402. /*------------------------------------------------------------------------------
  403.  * Print out the time.
  404.  *----------------------------------------------------------------------------*/
  405. char *
  406. ngPrintTime(
  407.     void
  408.     )
  409. {
  410.     static char    timestr[64] ;
  411.     time_t        clock_val ;
  412.  
  413.     clock_val = time( NULL ) ;
  414.  
  415.     cftime( timestr, "%r", &clock_val ) ;
  416.  
  417.     return( timestr ) ;
  418. }
  419.  
  420.  
  421.  
  422. /*------------------------------------------------------------------------------
  423.  * Returns non-zero if the maximum number of acknowledgement packets have
  424.  * not been sent.
  425.  *----------------------------------------------------------------------------*/
  426. void
  427. ngAcknowledge(
  428.     long        hostId,
  429.     unsigned short    key
  430.     )
  431. {
  432.     struct ngHost    *host ;
  433.  
  434.     if( key != gameKey )
  435.     {
  436.         return ;
  437.     }
  438.  
  439.     if( ( host = findHost( hostId ) ) == NULL )
  440.     {
  441.         if( ( host = addHost( hostId ) ) == NULL )
  442.         {
  443.             perror( "should_acknowledge" ) ;
  444.             endProgram( 1 ) ;
  445.         }
  446.     }
  447.  
  448.     if( host->acks < MAX_ACKS && host->valid == NG_VALID_UNKNOWN )
  449.     {
  450.         host->acks++ ;
  451.         if( host->acks == 1 )
  452.         {
  453.             ngDprintf( "%s: should acknowledge %s.\n",
  454.                 ngPrintTime(), ngHostNameFromId( hostId ) );
  455.         }
  456.  
  457.         sendAcknowledgement( hostId ) ;
  458.     }
  459.  
  460.     return ;
  461. }
  462.  
  463.  
  464.  
  465. /*------------------------------------------------------------------------------
  466.  * Send out an acknowledgement packet.
  467.  *----------------------------------------------------------------------------*/
  468. static void
  469. sendAcknowledgement(
  470.     long    hostId
  471.     )
  472. {
  473.     struct ngAck    ack ;
  474.     
  475.     ack.magic = ackMagic ;
  476.     ack.id = selfId ;
  477.     ack.ackId = hostId ;
  478.     ack.key = selfKey ;
  479.     sendOut( &ack, sizeof( ack ) ) ;
  480. }
  481.  
  482.  
  483.  
  484. /*------------------------------------------------------------------------------
  485.  * Remove a host from the host list.
  486.  *----------------------------------------------------------------------------*/
  487. void
  488. ngRemoveHost(
  489.     long    hostId
  490.     )
  491. {
  492.     struct ngHost    *host = firstHost ;
  493.     struct ngHost    *prev = NULL ;
  494.  
  495.     while( host != NULL && host->id != hostId )
  496.     {
  497.         prev = host ;
  498.         host = host->next ;
  499.     }
  500.  
  501.     if( host == NULL )
  502.         return ;
  503.  
  504.     if( host->id == hostId )
  505.     {
  506.         /*
  507.          * If prev == NULL, this means we're removing the first host.
  508.          * Set the firstHost to point to the next host.
  509.          */
  510.         if( prev == NULL )
  511.         {
  512.             firstHost = host->next ;
  513.         }
  514.         /*
  515.          * We're past the first host, so link up the previous one to
  516.          * the next one.
  517.          */
  518.         else
  519.         {
  520.             prev->next = host->next ;
  521.         }
  522.         free( host ) ;
  523.     }
  524.  
  525.     return ;
  526. }
  527.  
  528.  
  529.  
  530. /*------------------------------------------------------------------------------
  531.  * Note that a invalid host quit.
  532.  *---------------------------------------------------------------------------*/
  533. void
  534. ngQuitHost(
  535.     long    hostId
  536.     )
  537. {
  538.     struct ngHost    *host ;
  539.     struct ngAck    invalid ;
  540.  
  541.     if( ( host = findHost( hostId ) ) == NULL )
  542.     {
  543.         return ;
  544.     }
  545.  
  546.     if( host->valid != NG_VALID_QUIT && host->quits < MAX_QUITS )
  547.     {
  548.         host->quits += 1 ;
  549.         host->last_valid = host->valid ;
  550.         host->valid = NG_VALID_QUIT ;
  551.         host->acks = 0 ;
  552.         ngDprintf( "%s: %s quit\n", ngPrintTime(),
  553.             ngHostNameFromId( hostId ) ) ;
  554.     }
  555. }
  556.  
  557.  
  558.  
  559. /*------------------------------------------------------------------------------
  560.  * I've been dropped by a host, so indicate status with that host so that I
  561.  * can know when it starts receiving me again.
  562.  *---------------------------------------------------------------------------*/
  563. void
  564. ngDroppedByHost(
  565.     long    hostId
  566.     )
  567. {
  568.     struct ngHost    *host ;
  569.  
  570.     if( ( host = findHost( hostId ) ) != NULL )
  571.     {
  572.         host->valid = NG_DROPPED ;
  573.         ngDprintf( "%s: removing %s from game (dropped by him)\n",
  574.                   ngPrintTime(), ngHostNameFromId( hostId ) ) ;
  575.     }
  576. }
  577.  
  578.  
  579.  
  580. /*------------------------------------------------------------------------------
  581.  * Invalidate a host.  This is permanent.  Will ignore all future packets
  582.  * from this host (until it quits).
  583.  *---------------------------------------------------------------------------*/
  584. void
  585. ngInvalidateHost(
  586.     long    hostId
  587.     )
  588. {
  589.     int            i ;
  590.     int            j ;
  591.     struct ngHost        *host ;
  592.     struct ngAck        invalid ;
  593.     struct ngCensusList    *cen ;
  594.  
  595.     if( ( host = findHost( hostId ) ) != NULL )
  596.     {
  597.         host->valid = NG_INVALID ;
  598.  
  599.         /*
  600.          * Notify host of being invalidated.
  601.          */
  602.         invalid.magic = invalidateMagic ;
  603.         invalid.id = selfId ;
  604.         invalid.ackId = hostId ;
  605.         invalid.key = gameKey ;
  606.         sendOut( &invalid, sizeof( invalid ) ) ;
  607.         if( ( cen = findHostCensus( hostId ) ) != NULL )
  608.         {
  609.             ngDprintf( "%s: %s invalidated --\n",
  610.                 ngPrintTime(), ngHostNameFromId( hostId ) ) ;
  611.             ngDprintf( "\tHe sees %ld hosts while you see %ld.\n",
  612.                 cen->census.nPlayers,
  613.                 myCensus.census.nPlayers ) ;
  614.             ngDprintf( "\tThe following hosts were seen by "
  615.                 "him:\n" ) ;
  616.             for( i = 0 ; i < cen->census.nPlayers ; i++ )
  617.             {
  618.                 if( cen->census.list[i] == selfId )
  619.                 {
  620.                     ngDprintf( "\t\t%s (valid - self)\n",
  621.                     ngHostNameFromId( cen->census.list[i]));
  622.                 }
  623.                 else
  624.                 {
  625.                     host = findHost( cen->census.list[i] ) ;
  626.                     if( host == NULL )
  627.                     {
  628.                         ngDprintf( "\t\t%s (do not see)"
  629.                             "\n", ngHostNameFromId(
  630.                             cen->census.list[i] ) );
  631.                     }
  632.                     else
  633.                     {
  634.                         showHostValidity( host ) ;
  635.                     }
  636.                 }
  637.             }
  638.             ngDprintf( "\tThe following hosts were seen by you but "
  639.                 "not validated by him:\n" ) ;
  640.  
  641.             j = 0 ;
  642.             for( i = 0 ; i < myCensus.census.nPlayers ; i++ )
  643.             {
  644.                 while( j < cen->census.nPlayers &&
  645.                     myCensus.census.list[i] >
  646.                     cen->census.list[j] )
  647.                 {
  648.                     j++ ;
  649.                 }
  650.                 if( j < cen->census.nPlayers &&
  651.                     myCensus.census.list[i] ==
  652.                     cen->census.list[j] )
  653.                 {
  654.                     j++ ;
  655.                 }
  656.                 else if( myCensus.census.list[i] != selfId )
  657.                 {
  658.                     ngDprintf( "\t\t%s\n",
  659.                         ngHostNameFromId(
  660.                         myCensus.census.list[i] ) ) ;
  661.                 }
  662.             }
  663.             ngDprintf( "end list for %s\n\n",
  664.                     ngHostNameFromId( hostId ) ) ;
  665.         }
  666.     }
  667. }
  668.  
  669.  
  670.  
  671. /*------------------------------------------------------------------------------
  672.  * Print out the valid of a host.
  673.  *----------------------------------------------------------------------------*/
  674. static void
  675. showHostValidity(
  676.     struct ngHost    *host
  677.     )
  678. {
  679.  
  680.     if( host->valid == NG_INVALID )
  681.     {
  682.         ngDprintf( "\t\t%s (invalidated)\n",
  683.                 ngHostNameFromId( host->id ) ) ;
  684.     }
  685.     else if( host->valid == NG_VALID_UNKNOWN )
  686.     {
  687.         if( host->drops )
  688.         {
  689.             ngDprintf( "\t\t%s (lost communication from)\n",
  690.                     ngHostNameFromId( host->id ) ) ;
  691.         }
  692.         else
  693.         {
  694.             ngDprintf( "\t\t%s (does not see me)\n",
  695.                     ngHostNameFromId( host->id ) ) ;
  696.         }
  697.     }
  698.     else if( host->valid == NG_VALID_QUIT )
  699.     {
  700.         if( host->last_valid == NG_VALID_QUIT )
  701.         {
  702.             ngDprintf( "\t\t%s (thought had quit)\n",
  703.                     ngHostNameFromId( host->id ) ) ;
  704.         }
  705.         else
  706.         {
  707.             host->valid = host->last_valid ;
  708.             showHostValidity( host ) ;
  709.             host->valid = NG_VALID_QUIT ;
  710.         }
  711.     }
  712.     else
  713.     {
  714.         ngDprintf( "\t\t%s (valid)\n",
  715.                 ngHostNameFromId( host->id ) ) ;
  716.     }
  717. }
  718.  
  719.  
  720.  
  721. /*------------------------------------------------------------------------------
  722.  * Send quit packets.
  723.  *----------------------------------------------------------------------------*/
  724. void
  725. ngSendQuitPacket(
  726.     void
  727.     )
  728. {
  729.     struct ngAck    ack ;
  730.  
  731.     ack.magic = quitMagic ;
  732.     ack.id    = selfId ;
  733.     ack.ackId = selfId ;
  734.     ack.key   = selfKey ;
  735.  
  736.     /*
  737.      * Send out 3 for good measure.
  738.      */
  739.     sendOut( &ack, sizeof( ack ) ) ;
  740.     sendOut( &ack, sizeof( ack ) ) ;
  741.     sendOut( &ack, sizeof( ack ) ) ;
  742.  
  743.     ngDprintf( "\n%s: quitting game\n", ngPrintTime() ) ;
  744.     validationSummary() ;
  745.  
  746.     sleep( 1 ) ;
  747. }
  748.  
  749.  
  750.  
  751. /*------------------------------------------------------------------------------
  752.  * Debug printf.
  753.  *----------------------------------------------------------------------------*/
  754. void
  755. ngDprintf(
  756.     char *fmt,
  757.     ...
  758.     )
  759. {
  760.     va_list        args ;
  761.     static int    status = 0 ;
  762.     static FILE    *debugOut ;
  763.  
  764.     if( status == -1 || ngLogFile == NULL )
  765.     {
  766.         return ;
  767.     }
  768.  
  769.     if( strcmp( fmt, "end" ) == 0 )
  770.     {
  771.         if( status == 1 )
  772.         {
  773.             fclose( debugOut ) ;
  774.             printf( "Summary of validations written to %s.\n",
  775.                 ngLogFile ) ;
  776.         }
  777.         return ;
  778.     }
  779.     else if( strcmp( fmt, "disable" ) == 0 )
  780.     {
  781.         status = -1 ;
  782.         return ;
  783.     }
  784.  
  785.     if( status == 0 )
  786.     {
  787.         if( ( debugOut = fopen( ngLogFile, "w" ) ) == NULL )
  788.         {
  789.             status = -1 ;
  790.             return ;
  791.         }
  792.         else
  793.         {
  794.             status = 1 ;
  795.         }
  796.     }
  797.  
  798.     va_start( args, fmt ) ;
  799.     vfprintf( debugOut, fmt, args ) ;
  800.     va_end( args ) ;
  801. }
  802.  
  803.  
  804.  
  805. /*------------------------------------------------------------------------------
  806.  * Register the game's key code.
  807.  *----------------------------------------------------------------------------*/
  808. void
  809. ngRegisterKey(
  810.     unsigned short    key
  811.     )
  812. {
  813.     gameKey = key ;
  814. }
  815.  
  816.  
  817.  
  818. /*------------------------------------------------------------------------------
  819.  * Generate the census checksum (both xor and summation) from the list of
  820.  * hostid's in the census host list.
  821.  *----------------------------------------------------------------------------*/
  822. static void
  823. generateCensusChecksum(
  824.     void
  825.     )
  826. {
  827.     int    i ;
  828.  
  829.     myCensus.census.chksum = 0 ;
  830.     myCensus.census.chkxor = 0 ;
  831.     for( i = 0 ; i < myCensus.census.nPlayers ; i++ )
  832.     {
  833.         myCensus.census.chksum = ( myCensus.census.chksum +
  834.             ( myCensus.census.list[i] & 0x3fffffff ) ) &
  835.             0x3fffffff ;
  836.         myCensus.census.chkxor ^= myCensus.census.list[i] ;
  837.     }
  838. }
  839.  
  840.  
  841.  
  842. /*------------------------------------------------------------------------------
  843.  * Initialize the internal census packet.
  844.  *----------------------------------------------------------------------------*/
  845. void
  846. ngInitCensusPacket(
  847.     long    id
  848.     )
  849. {
  850.     myCensus.census.magic    = censusMagic ;
  851.     myCensus.census.id       = id ;
  852.     myCensus.census.nPlayers = 1 ;
  853.     myCensus.census.list[0]  = id ;
  854.     myCensus.next         = NULL ;
  855.     copyTime( &last_census_sent, ¤tTime ) ;
  856.  
  857.     setTime( &weedTime, ¤tTime, NG_WEEDING_TIME, 0 ) ;
  858.     needToDoWeeding = FALSE ;
  859.  
  860.     generateCensusChecksum() ;
  861. }
  862.  
  863.  
  864.  
  865. /*------------------------------------------------------------------------------
  866.  * Move bytes from one source to another, allowing for overlap
  867.  *----------------------------------------------------------------------------*/
  868. static void
  869. copyBytes(
  870.     void    *src,
  871.     void    *dst,
  872.     long    n
  873.     )
  874. {
  875.     char    *s ;
  876.     char    *d ;
  877.  
  878.     if( src > dst )
  879.     {
  880.         s = (char *)src ;
  881.         d = (char *)dst ;
  882.         while( n-- )
  883.             *(d++) = *(s++) ;
  884.     }
  885.     else
  886.     {
  887.         s = (char *)src + n ;
  888.         d = (char *)dst + n ;
  889.         while( n-- )
  890.             *(--d) = *(--s) ;
  891.     }
  892. }
  893.  
  894.  
  895.  
  896. /*------------------------------------------------------------------------------
  897.  * Add a host to our census list.
  898.  *----------------------------------------------------------------------------*/
  899. void
  900. ngAddHostToCensus(
  901.     long    id
  902.     )
  903. {
  904.     int            n ;
  905.     int            i ;
  906.     int            insertPt ;
  907.     static int        first = 1 ;
  908.     struct ngCensusList    *next = myCensus.next ;
  909.     struct ngCensusList    *prev = &myCensus ;
  910.  
  911.     if( findHostInMyCensusList( id ) != -1 )
  912.     {
  913.         return ;
  914.     }
  915.  
  916.     if( ( i = findPlayer( id ) ) == -1 )
  917.     {
  918.         return ;
  919.     }
  920.  
  921.     n = (myCensus.census.nPlayers)++ ;
  922.  
  923.     while( next )
  924.     {
  925.         prev = next ;
  926.         next = next->next ;
  927.     }
  928.  
  929.     if( ( next = calloc( 1, sizeof( struct ngCensusList ) ) ) == NULL )
  930.     {
  931.         perror( "ngAddHostToCensus" ) ;
  932.         endProgram( 1 ) ;
  933.     }
  934.  
  935.     prev->next = next ;
  936.  
  937.     next->census.id = id ;
  938.     next->census.nPlayers = 1 ;
  939.     next->census.chksum = id ;
  940.     next->census.chkxor = id ;
  941.  
  942.     insertPt = n ;
  943.     while( insertPt && myCensus.census.list[insertPt-1] > id )
  944.         insertPt-- ;
  945.  
  946.     copyBytes( &(myCensus.census.list[insertPt]),
  947.            &(myCensus.census.list[insertPt+1]),
  948.            ( n - insertPt ) * sizeof( myCensus.census.list[0] ) ) ;
  949.  
  950.     myCensus.census.list[insertPt] = id ;
  951.  
  952.     generateCensusChecksum() ;
  953.  
  954.     if( !first )
  955.     {
  956.         resetDeadlines( i ) ;
  957.     }
  958.     first = 0 ;
  959.  
  960.     setTime( &weedTime, ¤tTime, NG_WEEDING_TIME, 0 ) ;
  961.     needToDoWeeding = TRUE ;
  962.  
  963.     setTime( &(deadline[i]), ¤tTime, NG_NEW_DEADLINE_TIME, 0 ) ;
  964.     ngDprintf( "%s: added %s to game and census\n", ngPrintTime(),
  965.             ngHostNameFromId( id ) ) ;
  966.  
  967.     needToSendCensus = TRUE ;
  968. }
  969.  
  970.  
  971.  
  972. /*------------------------------------------------------------------------------
  973.  * Find a host in the linked list of census packets.
  974.  *----------------------------------------------------------------------------*/
  975. struct ngCensusList *
  976. findHostCensus(
  977.     long    id
  978.     )
  979. {
  980.     struct ngCensusList    *next = myCensus.next ;
  981.  
  982.     while( next && next->census.id != id )
  983.     {
  984.         next = next->next ;
  985.     }
  986.  
  987.     return( next ) ;
  988. }
  989.  
  990.  
  991.  
  992. /*------------------------------------------------------------------------------
  993.  * Locate a host within our census list.
  994.  *----------------------------------------------------------------------------*/
  995. static int
  996. findHostInMyCensusList(
  997.     long    id
  998.     )
  999. {
  1000.     int    n ;
  1001.     int    lo = 0 ;
  1002.     int    hi = myCensus.census.nPlayers - 1 ;
  1003.     long    *census_list = myCensus.census.list ;
  1004.  
  1005.     while( 1 )
  1006.     {
  1007.         n = ( lo + hi ) / 2 ;
  1008.         if( id == census_list[n] )
  1009.         {
  1010.             return( n ) ;
  1011.         }
  1012.         else if( id < census_list[n] )
  1013.         {
  1014.             hi = n - 1 ;
  1015.         }
  1016.         else
  1017.         {
  1018.             lo = n + 1 ;
  1019.         }
  1020.         if( lo > hi )
  1021.         {
  1022.             return( -1 ) ;
  1023.         }
  1024.     }
  1025. }
  1026.  
  1027.  
  1028.  
  1029. /*------------------------------------------------------------------------------
  1030.  * Compare a census packet with another.
  1031.  *----------------------------------------------------------------------------*/
  1032. static int
  1033. compareCensus(
  1034.     struct ngCensus    *input
  1035.     )
  1036. {
  1037.     return( ( input->nPlayers == myCensus.census.nPlayers ) &&
  1038.         ( input->chksum == myCensus.census.chksum ) &&
  1039.         ( input->chkxor == myCensus.census.chkxor ) ) ;
  1040. }
  1041.  
  1042.  
  1043.  
  1044. /*------------------------------------------------------------------------------
  1045.  * Process a census packet.
  1046.  *----------------------------------------------------------------------------*/
  1047. void processNgCensus(
  1048.     struct ngCensus    *input
  1049.     )
  1050. {
  1051.     int            n ;
  1052.     int            i ;
  1053.     struct ngCensusList    *pcen ;
  1054.     struct ngHost        *host ;
  1055.  
  1056.     if( ( n = findPlayer( input->id ) ) > 0 )
  1057.     {
  1058.         /*
  1059.          * Save the census packet.
  1060.          */
  1061.         if( ( pcen = findHostCensus( input->id ) ) == NULL )
  1062.         {
  1063. #if defined( NETDEBUGGER )
  1064.             post_new_message( 0, 0, "could not find CENSUS for %c",
  1065.                               'a' + input->id ) ;
  1066. #endif /* defined( NETDEBUGGER ) */
  1067.             return ;
  1068.         }
  1069.         else
  1070.         {
  1071.             copyBytes( input, &(pcen->census),
  1072.                     sizeof( struct ngCensus ) ) ;
  1073.         }
  1074.         /*
  1075.          * Check if census matches up with SELF's census.
  1076.          */
  1077.         if( compareCensus( input ) )
  1078.         {
  1079.             /*
  1080.              * It matches, so set new deadline for player.
  1081.              */
  1082.             setTime( &(deadline[n]), ¤tTime,
  1083.                 NG_OLD_DEADLINE_TIME, 0 ) ;
  1084.             if( ( host = findHost( input->id ) ) != NULL )
  1085.             {
  1086.                 if( host->agrees == 0 )
  1087.                 {
  1088.                     ngDprintf( "%s: validation now matching"
  1089.                         " from %s.\n", ngPrintTime(),
  1090.                         ngHostNameFromId( host->id ) ) ;
  1091.                     host->agrees = 1 ;
  1092.                 }
  1093.             }
  1094.         }
  1095.         else
  1096.         {
  1097.             /*
  1098.              * No match.  See if player's deadline has been
  1099.              * exceeded.
  1100.              */
  1101.             if( ( host = findHost( input->id ) ) != NULL )
  1102.             {
  1103.                 if( host->agrees == 1 )
  1104.                 {
  1105.                     ngDprintf( "%s: validation no longer "
  1106.                         "matches from %s.\n",
  1107.                         ngPrintTime(),
  1108.                         ngHostNameFromId( host->id ) ) ;
  1109.                     host->agrees = 0 ;
  1110.                 }
  1111.             }
  1112.             if( elapsedSecTenths( &(deadline[n]), ¤tTime )
  1113.                 > 0 )
  1114.             {
  1115.                 /*
  1116.                  * Deadline passed, drop player.
  1117.                  */
  1118.                 ngInvalidateHost( input->id ) ;
  1119.                 deletePlayer( n, "was invalidated" ) ;
  1120.             }
  1121.  
  1122.             /*
  1123.              * Set time between census broadcasts to a smaller
  1124.              * number since the census lists are in flux.
  1125.              */
  1126.             timeBetweenCensusPosts = NG_UNSTEADY_CENSUS_RATE ;
  1127.         }
  1128.     }
  1129. }
  1130.  
  1131.  
  1132.  
  1133. /*------------------------------------------------------------------------------
  1134.  * Check to see if we need to send a census packet.
  1135.  *----------------------------------------------------------------------------*/
  1136. void
  1137. checkToSendCensus(
  1138.     void
  1139.     )
  1140. {
  1141.     long    tenths ;
  1142.  
  1143.     if( needToDoWeeding &&
  1144.         elapsedSecTenths( &weedTime, ¤tTime ) > 0 )
  1145.     {
  1146.         doWeeding() ;
  1147.         needToDoWeeding = FALSE ;
  1148.     }
  1149.  
  1150.     if( needToSendCensus ||
  1151.         elapsedSecTenths( &last_census_sent, ¤tTime ) >
  1152.         timeBetweenCensusPosts )
  1153.     {
  1154.         needToSendCensus = FALSE ;
  1155.         sendOut( &(myCensus.census), sizeof( struct ngCensus ) ) ;
  1156.         copyTime( &last_census_sent, ¤tTime ) ;
  1157.         timeBetweenCensusPosts = NG_STEADY_CENSUS_RATE ;
  1158.     }
  1159. }
  1160.  
  1161.  
  1162.  
  1163. /*------------------------------------------------------------------------------
  1164.  * Reset all of the census deadlines except for the newest.
  1165.  *----------------------------------------------------------------------------*/
  1166. static void
  1167. resetDeadlines(
  1168.     int    except
  1169.     )
  1170. {
  1171.     int    i ;
  1172.     long    secs ;
  1173.     long    tenths ;
  1174.  
  1175.     if( ( secs = elapsedSecTenths( &last_census_sent, ¤tTime ) ) <
  1176.             NG_OLD_DEADLINE_TIME * 10 )
  1177.     {
  1178.  
  1179.         tenths = secs % 10 ;
  1180.         secs /= 10 ;
  1181.  
  1182.         for( i = ENEMY ; i < numberPlayers ; i++ )
  1183.         {
  1184.             if( i != except )
  1185.             {
  1186.                 setTime( &(deadline[i]), &(deadline[i]), secs,
  1187.                     tenths ) ;
  1188.             }
  1189.         }
  1190.     }
  1191. }
  1192.  
  1193.  
  1194.  
  1195. /*------------------------------------------------------------------------------
  1196.  * Print out a list of all non-valid hosts and a guess at why.
  1197.  *----------------------------------------------------------------------------*/
  1198. void
  1199. validationSummary( void )
  1200. {
  1201.     struct ngHost    *host = firstHost ;
  1202.  
  1203.     while( host )
  1204.     {
  1205.         switch( host->valid )
  1206.         {
  1207.  
  1208.             case NG_VALID_UNKNOWN :
  1209.                 if( host->drops )
  1210.                 {
  1211.                     ngDprintf( "Lost communcation from host"
  1212.                         " %s.\n",
  1213.                         ngHostNameFromId( host->id ) ) ;
  1214.                 }
  1215.                 else
  1216.                 {
  1217.                     ngDprintf( "Host %s evidently could not"
  1218.                         "receive my packets.\n",
  1219.                         ngHostNameFromId( host->id ) ) ;
  1220.                 }
  1221.                 break ;
  1222.  
  1223.             case NG_VALID :
  1224.                 ngDprintf( "Host %s was valid.\n",
  1225.                         ngHostNameFromId( host->id ) ) ;
  1226.                 break ;
  1227.  
  1228.             case NG_INVALID :
  1229.                 break ;
  1230.  
  1231.             case NG_VALID_QUIT :
  1232.                 ngDprintf( "Host %s has quit (validity "
  1233.                     "unknown).\n",
  1234.                     ngHostNameFromId( host->id ) ) ;
  1235.                 break ;
  1236.  
  1237.             case NG_DROPPED :
  1238.                 ngDprintf( "Host %s evidently stopped receiving"
  1239.                     "my packets.\n",
  1240.                     ngHostNameFromId( host->id ) ) ;
  1241.                 break ;
  1242.  
  1243.             default :
  1244.                 ngDprintf( "Odd validity for %s.\n",
  1245.                     ngHostNameFromId( host->id ) ) ;
  1246.                 break ;
  1247.         }
  1248.         host = host->next ;
  1249.     }
  1250.  
  1251.     ngDprintf( "end" ) ;
  1252. }
  1253.  
  1254.  
  1255.  
  1256. /*------------------------------------------------------------------------------
  1257.  * Convert a `hostid' number to a string representing the host's name if
  1258.  * known, otherwise the IP address.
  1259.  *----------------------------------------------------------------------------*/
  1260. char
  1261. *ngHostNameFromId(
  1262.     long    id
  1263.     )
  1264. {
  1265.     struct in_addr    addr ;
  1266.     struct hostent    *hp ;
  1267.     static char    buffer[256] ;
  1268.  
  1269.     addr.s_addr = id ;
  1270.     hp = gethostbyaddr( &addr, sizeof(addr), AF_INET ) ;
  1271.     if( hp )
  1272.     {
  1273.         sprintf( buffer, "%s (%s)", hp->h_name, inet_ntoa(addr) ) ;
  1274.     }
  1275.     else
  1276.     {
  1277.         sprintf( buffer, "%s", inet_ntoa(addr) ) ;
  1278.     }
  1279.  
  1280.     return( buffer ) ;
  1281. }
  1282.  
  1283.  
  1284.  
  1285. /*------------------------------------------------------------------------------
  1286.  * Do initial weeding of host to try and join the largest group.
  1287.  *----------------------------------------------------------------------------*/
  1288. static void
  1289. doWeeding(
  1290.     void
  1291.     )
  1292. {
  1293.     int            i ;
  1294.     int            j ;
  1295.     int            n ;
  1296.     int            total ;
  1297.     int            best_total = 0 ;
  1298.     int            least_seen ;
  1299.     int            times_seen[MAXPLAYERS] ;
  1300.     struct ngCensusList    *next = myCensus.next ;
  1301.     struct ngCensusList    *prev = &myCensus ;
  1302.     struct ngCensusList    *best ;
  1303.  
  1304. #if defined( NETDEBUGGER )
  1305.     post_new_message( 0, 0, "%s doing initial weeding", ngPrintTime() ) ;
  1306. #endif /* defined( NETDEBUGGER ) */
  1307.  
  1308.     /*
  1309.      * Go through all players in my census and mark as being seen once
  1310.      * (by me).
  1311.      */
  1312.     for( i = 0 ; i < myCensus.census.nPlayers ; i++ )
  1313.     {
  1314.         times_seen[i] = 1 ;
  1315.     }
  1316.  
  1317.     /*
  1318.      * Go through all other players and mark players according to how many
  1319.      * times they appear on other's censuses.
  1320.      */
  1321.     while( next )
  1322.     {
  1323.         total = 0 ;
  1324.         for( i = 0 ; i < next->census.nPlayers ; i++ )
  1325.         {
  1326.             /*
  1327.              * Check if host is on my census.
  1328.              */
  1329.             n = findHostInMyCensusList( next->census.list[i] ) ;
  1330.             /*
  1331.              * If host is in my census, increment total and mark
  1332.              * player.
  1333.              */
  1334.             if( n != -1 )
  1335.             {
  1336.                 total++ ;
  1337.                 times_seen[n] += 1 ;
  1338.             }
  1339.         }
  1340.         /*
  1341.          * Check number of census matches.
  1342.          */
  1343.         if( total > best_total )
  1344.         {
  1345.             best = next ;
  1346.             best_total = total ;
  1347.         }
  1348.         next = next->next ;
  1349.     }
  1350.  
  1351. #if defined( NETDEBUGGER )
  1352.     for( i = 0 ; i < myCensus.census.nPlayers ; i++ )
  1353.     {
  1354.         post_new_message( 0, 0, "*        * %c seen %d times",
  1355.             'a' + myCensus.census.list[i], times_seen[i] ) ;
  1356.     }
  1357. #endif /* defined( NETDEBUGGER ) */
  1358.  
  1359.     /*
  1360.      * If the best match is greater than zero...
  1361.      */
  1362.     if( best_total > 0 && best != NULL )
  1363.     {
  1364.  
  1365. #if defined( NETDEBUGGER )
  1366.         post_new_message( 0, 0, "*        * best total = %d/%d "
  1367.             "(player %c)", best_total, best->census.nPlayers,
  1368.             'a' + best->census.id ) ;
  1369. #endif /* defined( NETDEBUGGER ) */
  1370.  
  1371.         /*
  1372.          * Compare my census with the best match.
  1373.          */
  1374.         j = 0 ;
  1375.         least_seen = best_total + 1 ;
  1376.         for( i = 0 ; i < myCensus.census.nPlayers ; i++ )
  1377.         {
  1378.             /*
  1379.              * Find location in best census where my i-th census
  1380.              * entry should be.
  1381.              */
  1382.             while( j < best->census.nPlayers &&
  1383.                 myCensus.census.list[i] > best->census.list[j] )
  1384.             {
  1385.                 j++ ;
  1386.             }
  1387.             /*
  1388.              * If i-th entry is in best census, check for least
  1389.              * seen value.
  1390.              */
  1391.             if( j < best->census.nPlayers &&
  1392.                 myCensus.census.list[i] == best->census.list[j])
  1393.             {
  1394.                 if( times_seen[i] <= least_seen )
  1395.                 {
  1396.                     least_seen = times_seen[i] ;
  1397.                 }
  1398.                 j++ ;
  1399.             }
  1400.             /*
  1401.              * The i-th entry isn't in the best census, so shorten
  1402.              * it's deadline.
  1403.              */
  1404.             else
  1405.             {
  1406.                 n = findPlayer( myCensus.census.list[i] ) ;
  1407.                 if( n > 0 )
  1408.                 {
  1409.                     setTime( &(deadline[n]), &(deadline[n]),
  1410.                         NG_DROPPING_DELTA, 0 ) ;
  1411. #if defined( NETDEBUGGER )
  1412.                     post_new_message( 0, 0, "*        * "
  1413.                         "should_drop %c (%s)",
  1414.                         'a' + myCensus.census.list[i],
  1415.                         ngPrintTimeHundreths(
  1416.                             &(deadline[n]) ) ) ;
  1417. #endif /* defined( NETDEBUGGER ) */
  1418.                 }
  1419.             }
  1420.         }
  1421.  
  1422. #if defined( NETDEBUGGER )
  1423.         post_new_message( 0, 0, "*        * least_seen = %d",
  1424.                 least_seen ) ;
  1425. #endif /* defined( NETDEBUGGER ) */
  1426.  
  1427.         /*
  1428.          * If some entry is seen less times than the best total, shorten
  1429.          * its deadline time.
  1430.          */
  1431.         if( least_seen < best_total )
  1432.         {
  1433.             for( i = 0 ; i < myCensus.census.nPlayers ; i++ )
  1434.             {
  1435.                 if( times_seen[i] == least_seen )
  1436.                 {
  1437.                     /*
  1438.                      * Decrease deadline.
  1439.                      */
  1440.                     n = findPlayer(
  1441.                         myCensus.census.list[i] ) ;
  1442.                     if( n > 0 )
  1443.                     {
  1444.                         setTime( &(deadline[n]),
  1445.                             &(deadline[n]),
  1446.                             NG_DROPPING_DELTA, 0 ) ;
  1447. #if defined( NETDEBUGGER )
  1448.                         post_new_message( 0, 0,
  1449.                             "*        * should_drop"
  1450.                             " %c (%s)", 'a' +
  1451.                             myCensus.census.list[i],
  1452.                             ngPrintTimeHundreths(
  1453.                             &(deadline[n]) ) ) ;
  1454. #endif /* defined( NETDEBUGGER ) */
  1455.                     }
  1456.                 }
  1457.             }
  1458.         }
  1459.     }
  1460.  
  1461. #if defined( NETDEBUGGER )
  1462.     post_new_message( 0, 0, "********** ending weeding" ) ;
  1463. #endif /* defined( NETDEBUGGER ) */
  1464. }
  1465.  
  1466.  
  1467.  
  1468. /*------------------------------------------------------------------------------
  1469.  * Print out the date.
  1470.  *----------------------------------------------------------------------------*/
  1471. char *
  1472. printDate(
  1473.     void
  1474.     )
  1475. {
  1476.     static char    datestr[64] ;
  1477.     time_t        clock_val ;
  1478.  
  1479.     clock_val = time( NULL ) ;
  1480.  
  1481.     cftime( datestr, "%D", &clock_val ) ;
  1482.  
  1483.     return( datestr ) ;
  1484. }
  1485.  
  1486.  
  1487.  
  1488. /*------------------------------------------------------------------------------
  1489.  * Print out the time with hundreths of seconds.
  1490.  *----------------------------------------------------------------------------*/
  1491. static char *
  1492. ngPrintTimeHundreths(
  1493.     struct timeval    *t
  1494.     )
  1495. {
  1496.     static char    timestr[64] ;
  1497.     int        i ;
  1498.     time_t        secs ;
  1499.     long        hsecs ;
  1500.  
  1501.     secs = t->tv_sec + t->tv_usec / 1000000 ;
  1502.     hsecs = ( t->tv_usec % 1000000 ) / 10000 ;
  1503.     cftime( timestr, "%I:%M:%S.00 %p", &secs ) ;
  1504.     i = strlen( timestr ) ;
  1505.  
  1506.     sprintf( timestr + 9, "%02d", hsecs ) ;
  1507.     timestr[11] = ' ' ;
  1508.  
  1509.     return( timestr ) ;
  1510. }
  1511.  
  1512.  
  1513.  
  1514. /*------------------------------------------------------------------------------
  1515.  * Copy over a timeval structure.
  1516.  *----------------------------------------------------------------------------*/
  1517. static void copyTime(
  1518.     struct timeval    *dst,
  1519.     struct timeval    *src
  1520.     )
  1521. {
  1522.     bcopy( src, dst, sizeof( struct timeval ) ) ;
  1523. }
  1524.  
  1525.  
  1526.  
  1527. /*------------------------------------------------------------------------------
  1528.  * Initialize a timeval structure.
  1529.  *----------------------------------------------------------------------------*/
  1530. static void setTime(
  1531.     struct timeval    *dst,
  1532.     struct timeval    *src,
  1533.     long        secs,
  1534.     long        usecs
  1535.     )
  1536. {
  1537.     copyTime( dst, src ) ;
  1538.     addToTime( dst, secs, usecs ) ;
  1539. }
  1540.  
  1541.  
  1542.  
  1543. /*------------------------------------------------------------------------------
  1544.  * Add an offset to a timeval structure.
  1545.  *----------------------------------------------------------------------------*/
  1546. static void
  1547. addToTime(
  1548.     struct timeval    *dst,
  1549.     long        secs,
  1550.     long        usecs
  1551.     )
  1552. {
  1553.     dst->tv_usec += usecs ;
  1554.     dst->tv_sec  += secs + ( dst->tv_usec / 1000000 ) ;
  1555.     dst->tv_usec %= 1000000 ;
  1556. }
  1557.  
  1558.  
  1559.  
  1560. /*------------------------------------------------------------------------------
  1561.  * Return the number of tenths of seconds elapsed since a specified time.
  1562.  *----------------------------------------------------------------------------*/
  1563. static long
  1564. elapsedSecTenths(
  1565.     struct timeval    *bufThen,
  1566.     struct timeval    *bufNow
  1567.     )
  1568. {
  1569.     struct timeval tv ;
  1570.     struct timeval *now ;
  1571.     struct timeval *then = bufThen ;
  1572.     struct timezone tzp ;
  1573.  
  1574.     if( bufNow != NULL )
  1575.     {
  1576.         now = bufNow ;
  1577.     }
  1578.     else
  1579.     {
  1580.         now = &tv ;
  1581.         gettimeofday( now, &tzp ) ;
  1582.     }
  1583.  
  1584.     return( ( ( now->tv_sec - then->tv_sec ) * 10 +
  1585.         ( now->tv_usec - then->tv_usec ) / 100000 ) ) ;
  1586. }
  1587.  
  1588.  
  1589.  
  1590.